//**************************************************
// Description: extract UDP field from N*128-bit data 
//**************************************************
// note: do not execute CRC check !

//**************************************************
// include files
//**************************************************
`include "protocol_define.v"

module execute #(
    parameter IN_DATA_WIDTH     = 128   ,
    parameter IN_MOD_WIDTH      = 4     ,
    parameter OUT_ORDER_WIDTH   = 30    ,
    parameter OUT_ADDR_WIDTH    = 32    ,
    parameter OUT_DATA_WIDTH    = 32    
        )(
    input                           clk ,
    input                           rst_n ,
    // pre-module(UDP deal) interface
    output                          PROTOCOL_rdy ,
    input   [IN_DATA_WIDTH-1:0]     PROTOCOL_data ,
    input   [IN_MOD_WIDTH-1:0]      PROTOCOL_mod ,
    input                           PROTOCOL_sav ,
    input                           PROTOCOL_val ,
    input                           PROTOCOL_sop ,
    input                           PROTOCOL_eop ,
    // action addition
    input   [31:0]                  NP_action_addition ,
    // post-module(async FIFO) interface
    output                          ACTION_wren ,
    output  [OUT_ORDER_WIDTH-1:0]   ACTION_order ,
    output  [OUT_ADDR_WIDTH+1:0]    ACTION_addr ,   // the [MSB] means whether it is the first action in a frame 
                                                    // the [MSB-1]=1 means "write", MSB=0 means "read"
    output  [OUT_DATA_WIDTH-1:0]    ACTION_data 
) ;
//**************************************************
// FSM
//**************************************************
    //----------------------------------------------
    // signal declare
    //----------------------------------------------
    reg                             execute_is_done ;  // end the FSM state (predition)
    wire                            frame_start_w ; // assert if frame start
    // data delay
    reg     [IN_DATA_WIDTH-1:0]     PROTOCOL_data_ff1 ;
    reg     [IN_MOD_WIDTH-1:0]      PROTOCOL_mod_ff1 ;
    reg     [IN_DATA_WIDTH-1:0]     PROTOCOL_data_ff2 ;
    reg     [IN_MOD_WIDTH-1:0]      PROTOCOL_mod_ff2 ;
    // eop delay
    reg                             PROTOCOL_eop_ff1 ;
    // output register
    reg                             ACTION_wren_r ;
    reg     [OUT_ORDER_WIDTH-1:0]   ACTION_order_r ;
    reg     [OUT_ADDR_WIDTH+1:0]    ACTION_addr_r ;
    reg     [OUT_DATA_WIDTH-1:0]    ACTION_data_r ;

    //----------------------------------------------
    // FSM declare
    //----------------------------------------------
    reg     [2:0]                   execute_state_c ;
    reg     [2:0]                   execute_state_c_ff1 ;
    reg     [2:0]                   execute_state_n ;
    localparam EXECUTE_IDLE =       3'd0 ;
    localparam EXECUTE_WAIT =       3'd1 ;
    localparam EXECUTE_EXTRACT =    3'd2 ;
    localparam EXECUTE_END =        3'd3 ;

    // FSM(1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            execute_state_c <= EXECUTE_IDLE ;
        else 
            execute_state_c <= execute_state_n ;
    end
    // FSM(2)
    always @(*) begin
        case (execute_state_c)
            EXECUTE_IDLE: begin
                if( PROTOCOL_sav == 1'b1 )
                    execute_state_n = EXECUTE_WAIT ;
                else
                    execute_state_n = EXECUTE_IDLE ;
            end

            EXECUTE_WAIT: begin
                if( PROTOCOL_sop == 1'b1 )
                    execute_state_n = EXECUTE_EXTRACT ;
                else
                    execute_state_n = EXECUTE_WAIT ;
            end

            EXECUTE_EXTRACT: begin
                if( execute_is_done == 1'b1 )
                    execute_state_n = EXECUTE_END ;
                else
                    execute_state_n = EXECUTE_EXTRACT ;
            end

            EXECUTE_END: begin
                if( PROTOCOL_val == 1'b0 )
                    execute_state_n = EXECUTE_IDLE ;
                else
                    execute_state_n = EXECUTE_END ;
            end

            default: 
                execute_state_n = EXECUTE_IDLE ;
        endcase
    end
    // FSM(3)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            ACTION_wren_r <= 1'b0 ;
            ACTION_order_r <= {(OUT_ORDER_WIDTH){1'b0}} ;
            ACTION_addr_r <= {(OUT_ADDR_WIDTH+2){1'b0}} ;
            ACTION_data_r <= {(OUT_DATA_WIDTH){1'b0}} ;
        end
        else begin
            if( (execute_state_c_ff1==EXECUTE_WAIT) && (execute_state_c==EXECUTE_EXTRACT) ) begin
                ACTION_wren_r <= 1'b1 ;
                ACTION_order_r <= PROTOCOL_data_ff1 [(IN_DATA_WIDTH-1)                                  -:  OUT_ORDER_WIDTH] ;
                ACTION_addr_r <= {frame_start_w , PROTOCOL_data_ff1[(IN_DATA_WIDTH-1-OUT_ORDER_WIDTH)] , PROTOCOL_data_ff1[(IN_DATA_WIDTH-1-OUT_ORDER_WIDTH-2)-:OUT_ADDR_WIDTH]} ;
                ACTION_data_r <=  PROTOCOL_data_ff1 [(IN_DATA_WIDTH-1-OUT_ORDER_WIDTH-2-OUT_ADDR_WIDTH) -:  OUT_DATA_WIDTH] ;
            end
            else if( (execute_state_c==EXECUTE_EXTRACT) && (execute_is_done!=1'b1) ) begin
                ACTION_wren_r <= 1'b1 ;
                ACTION_order_r <= PROTOCOL_data_ff1 [(IN_DATA_WIDTH-1)                                  -:  OUT_ORDER_WIDTH] ;
                ACTION_addr_r <= {frame_start_w , PROTOCOL_data_ff1[(IN_DATA_WIDTH-1-OUT_ORDER_WIDTH)] , PROTOCOL_data_ff1[(IN_DATA_WIDTH-1-OUT_ORDER_WIDTH-2)-:OUT_ADDR_WIDTH]} ;
                ACTION_data_r <=  PROTOCOL_data_ff1 [(IN_DATA_WIDTH-1-OUT_ORDER_WIDTH-2-OUT_ADDR_WIDTH) -:  OUT_DATA_WIDTH] ;
            end
            else begin
                ACTION_wren_r <= 1'b0 ;
                ACTION_order_r <= {(OUT_ORDER_WIDTH){1'b0}} ;
                ACTION_addr_r <= {(OUT_ADDR_WIDTH+2){1'b0}} ;
                ACTION_data_r <= {(OUT_DATA_WIDTH){1'b0}} ;
            end
        end
    end
    // signal delay
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            PROTOCOL_data_ff1 <= {(IN_DATA_WIDTH){1'b0}} ;
            PROTOCOL_mod_ff1 <= {(IN_MOD_WIDTH){1'b0}} ;
            PROTOCOL_data_ff2 <= {(IN_DATA_WIDTH){1'b0}} ;
            PROTOCOL_mod_ff2 <= {(IN_MOD_WIDTH){1'b0}} ;
        end
        else begin
            PROTOCOL_data_ff1 <= PROTOCOL_data ;
            PROTOCOL_mod_ff1 <= PROTOCOL_mod ;
            PROTOCOL_data_ff2 <= PROTOCOL_data_ff1 ;
            PROTOCOL_mod_ff2 <= PROTOCOL_mod_ff1 ;
        end
    end
    // state ready
    assign PROTOCOL_rdy = ( (execute_state_c==EXECUTE_IDLE) || (execute_state_c==EXECUTE_WAIT) )?1'b1:1'b0 ;

    //predition the end of frame
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            execute_is_done <= 1'b0 ;
        else begin
            if( (PROTOCOL_eop == 1'b1) && ( (PROTOCOL_mod<12) && (PROTOCOL_mod!=0) ) ) // 12-bytes = (30+2+32+32)/8
                execute_is_done <= 1'b1 ;
            else if( (PROTOCOL_data_ff1[31:0] != NP_action_addition) && (execute_state_c==EXECUTE_EXTRACT) ) 
                execute_is_done <= 1'b1 ;
            else
                execute_is_done <= PROTOCOL_eop_ff1 ;
        end
    end
    // eop delay
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            PROTOCOL_eop_ff1 <= 1'b0 ;
        else 
            PROTOCOL_eop_ff1 <= PROTOCOL_eop ;
    end

    // frame start judge
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            execute_state_c_ff1 <= EXECUTE_IDLE ;
        else
            execute_state_c_ff1 <= execute_state_c ;
    end
    assign frame_start_w = ( (execute_state_c==EXECUTE_EXTRACT) && (execute_state_c_ff1==EXECUTE_WAIT) ) ? 1'b1: 1'b0 ;



    // output signal assign
    assign ACTION_wren = ACTION_wren_r ;
    assign ACTION_order = ACTION_order_r ;
    assign ACTION_addr = ACTION_addr_r ;
    assign ACTION_data = ACTION_data_r ;
endmodule
